package wo.jpa.reader.entity;

import java.lang.reflect.Field;
import java.lang.reflect.Modifier;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;

import javax.persistence.Id;
import javax.persistence.ManyToMany;
import javax.persistence.ManyToOne;
import javax.persistence.OneToMany;
import javax.persistence.OneToOne;
import javax.persistence.Table;
import javax.persistence.Transient;

import org.apache.log4j.LogManager;
import org.apache.log4j.Logger;

import wo.common.exception.WoException;
import wo.common.util.WoJavaDocUtil;
import wo.common.util.WoUtil;
import wo.jpa.reader.util.JPAReaderConstant;
import wo.jpa.reader.util.JPAReaderUtil;

public class WoJPAEntity {

	private final static Logger LOG = LogManager.getLogger(WoJPAEntity.class);

	public WoJPAEntity(Class<?> eCls) {
		this.entityClass = eCls.getName();
		this.entityType = eCls;
		init();
	}

	public WoJPAEntity(String entityClass) {
		this.entityClass = entityClass;
		try {
			entityType = Thread.currentThread().getContextClassLoader().loadClass(entityClass);
		} catch (ClassNotFoundException e) {
			throw new WoException(e, JPAReaderConstant.ERROR_CLASS, entityClass);
		}
		init();
	}

	/**
	 * 异步初始化，当列和关系数据获取时，再初始化其值
	 */
	private void lazyInit () {
		if (keys.size() > 0) {
			return;
		}
		Field[] fields = entityType.getDeclaredFields();
		for (Field f : fields) {
			if (Modifier.isStatic(f.getModifiers())) {
				continue;
			}
			Transient t = f.getDeclaredAnnotation(Transient.class);
			if (t != null) {
				continue;
			}
			OneToMany o2m = f.getDeclaredAnnotation(OneToMany.class);
			if (o2m != null) {
				this.relations12M.add(new WoJPARelation12M(entityType, f));
				continue;
			}
			ManyToOne m2o = f.getDeclaredAnnotation(ManyToOne.class);
			if (m2o != null) {
				this.relationsM21.add(new WoJPARelationM21(entityType, f));
				continue;
			}
			ManyToMany m2m = f.getDeclaredAnnotation(ManyToMany.class);
			if (m2m != null) {
				this.relationsM2M.add(new WoJPARelationM2M(entityType, f));
				continue;
			}
			OneToOne o2o = f.getDeclaredAnnotation(OneToOne.class);
			if (o2o != null) {
				this.relations121.add(new WoJPARelation121(entityType, f));
				continue;
			}
			WoJPAField mf = new WoJPAField(f, table);
			mf.setEntity(this);
			if (f.getDeclaredAnnotation(Id.class) == null) {
				columns.add(mf);
			} else {
				keys.add(mf);
			}
		}
		readComment ();
	}
	
	private void readComment () {
		WoJavaDocUtil.read(entityClass);
		this.entityComment = WoJavaDocUtil.getClassComment();
		for (WoJPAField f : keys) {
			f.setFieldComment(WoJavaDocUtil.getFieldComment(f.getFieldName()));
		}
		for (WoJPAField f : columns) {
			f.setFieldComment(WoJavaDocUtil.getFieldComment(f.getFieldName()));
		}
		for (WoJPARelation r : relations121) {
			r.setSourceFieldComment(WoJavaDocUtil.getFieldComment(r.getSourceFieldName()));
		}
		for (WoJPARelation r : relations12M) {
			r.setSourceFieldComment(WoJavaDocUtil.getFieldComment(r.getSourceFieldName()));
		}
		for (WoJPARelation r : relationsM21) {
			r.setSourceFieldComment(WoJavaDocUtil.getFieldComment(r.getSourceFieldName()));
		}
		for (WoJPARelation r : relationsM2M) {
			r.setSourceFieldComment(WoJavaDocUtil.getFieldComment(r.getSourceFieldName()));
		}
	}
	
	/**
	 * @param cls
	 * @param propRelations 是否需要读取属性和关系
	 */
	private void init() {
		String[] attr = entityClass.split("[.]");
		this.entityName = attr[attr.length - 1];
		this.module = attr[attr.length - 3];
		table = entityType.getDeclaredAnnotation(Table.class);
		WoJavaDocUtil.read(entityClass);
		this.setEntityComment(WoJavaDocUtil.getClassComment());
	}

	private Object context;
	
	private String entityClass;

	private String entityName;

	private String entityComment;
	
	private String module;
	
	private Class<?> entityType;
	
	private List<WoJPARelation> relationsM21 = new ArrayList<WoJPARelation>();

	private List<WoJPARelation> relationsM2M = new ArrayList<WoJPARelation>();
	
	private List<WoJPARelation> relations121 = new ArrayList<WoJPARelation>();
	
	private List<WoJPARelation> relations12M = new ArrayList<WoJPARelation>();
	
	private List<WoJPAField> keys = new ArrayList<WoJPAField>();

	private List<WoJPAField> columns = new ArrayList<WoJPAField>();

	private Table table;

	/**
	 * 注释中的参数
	 */
	private Map<String, String> params = new HashMap<String, String>();
	
	public Object getContext() {
		return context;
	}

	public void setContext(Object context) {
		this.context = context;
	}

	public Class<?> getEntityType() {
		return entityType;
	}

	public void setEntityType(Class<?> entityType) {
		this.entityType = entityType;
	}

	public String getEntityName() {
		return entityName;
	}
	
	public String getEntityNameLowerFirstChar() {
		return WoUtil.getLowerFirstChar(entityName);
	}

	public void setEntityName(String entityName) {
		this.entityName = entityName;
	}

	public String getEntityComment() {
		return entityComment;
	}

	public void setEntityComment(String entityComment) {
		this.params = JPAReaderUtil.getParamsInComment(entityComment);
		this.entityComment = entityComment;
	}

	/**
	 * 从注释中获取中文名.
	 * @return
	 */
	public String getEntityCN() {
		return JPAReaderUtil.getNameInComment(entityComment, entityName);
	}
	
	public String getModule() {
		return module;
	}
	
	/**
	 * @return
	 */
	public String getModuleUpperFirstChar() {
		return WoUtil.getUpperFirstChar(module);
	}

	public void setModule(String module) {
		this.module = module;
	}

	public String getEntityClass() {
		return entityClass;
	}

	public void setEntityClass(String entityClass) {
		this.entityClass = entityClass;
	}

	/**
	 * 获取主键列
	 * 
	 * @return
	 */
	public List<WoJPAField> getKeys() {
		lazyInit ();
		return keys;
	}

	public void setKeys(List<WoJPAField> keys) {
		this.keys = keys;
	}

	/**
	 * 获取非主键列
	 * 
	 * @return
	 */
	public List<WoJPAField> getColumns() {
		lazyInit ();
		return columns;
	}

	/**
	 * 获取所有列
	 * 
	 * @return
	 */
	public List<WoJPAField> getAllColumns() {
		lazyInit ();
		List<WoJPAField> cols = new ArrayList<WoJPAField>();
		cols.addAll(keys);
		cols.addAll(columns);
		return cols;
	}

	public void setColumns(List<WoJPAField> columns) {
		this.columns = columns;
	}

	public Table getTable() {
		return table;
	}

	public void setTable(Table table) {
		this.table = table;
	}

	public List<WoJPARelation> getRelationsM21() {
		lazyInit ();
		return relationsM21;
	}

	public void setRelationsM21(List<WoJPARelation> relationsM21) {
		this.relationsM21 = relationsM21;
	}

	public List<WoJPARelation> getRelationsM2M() {
		lazyInit ();
		return relationsM2M;
	}

	public void setRelationsM2M(List<WoJPARelation> relationsM2M) {
		this.relationsM2M = relationsM2M;
	}

	public List<WoJPARelation> getRelations121() {
		lazyInit ();
		return relations121;
	}

	public void setRelations121(List<WoJPARelation> relations121) {
		this.relations121 = relations121;
	}

	public List<WoJPARelation> getRelations12M() {
		lazyInit ();
		return relations12M;
	}

	public void setRelations12M(List<WoJPARelation> relations12m) {
		relations12M = relations12m;
	}
	
	public String getDaoInterfaceFullName () {
		return this.getDaoPackage() + "." + this.getDaoInterfaceName();
	}
	
	public String getDaoInterfaceName () {
		return "I" + this.entityName + "Dao";
	}
	
	/**
	 * @param pkgName
	 * @return
	 */
	public String getSiblingPackage (String pkgName) {
		String[] arr = entityClass.split("[.]");
		// arr[arr.length - 2] = "dao";
		String pkg = "";
		for (int i = 0; i < arr.length - 2; i ++) {
			pkg += arr[i] + ".";
		}
		pkg += pkgName;
		return pkg;
	}
	
	public String getDaoPackage () {
		return getSiblingPackage("dao");
	}
	
	public String getJsPath () {
		return "/js/" + this.getModule() + "/" + this.getEntityNameLowerFirstChar();
	}
	
	public Set<String> getRelatedEntityClasses () {
		lazyInit ();
		Set<String> sets = new HashSet<String>();
		sets.add(this.entityClass);
		for (WoJPARelation r : this.relations121) {
			sets.add(r.getTarget().getEntityClass());
		}
		for (WoJPARelation r : this.relations12M) {
			sets.add(r.getTarget().getEntityClass());
		}
		for (WoJPARelation r : this.relationsM21) {
			sets.add(r.getTarget().getEntityClass());
		}
		for (WoJPARelation r : this.relationsM2M) {
			sets.add(r.getTarget().getEntityClass());
		}
		return sets;
	}
	
	public Set<String> getRelatedJsPaths () {
		lazyInit ();
		Set<String> sets = new HashSet<String>();
		/*
		for (WoJPARelation r : this.relations121) {
			sets.add(r.getTarget().getJsPath());
		}
		for (WoJPARelation r : this.relations12M) {
			sets.add(r.getTarget().getJsPath());
		}
		*/
		for (WoJPARelation r : this.relationsM21) {
			sets.add(r.getTarget().getJsPath());
		}
		for (WoJPARelation r : this.relationsM2M) {
			sets.add(r.getTarget().getJsPath());
		}
		return sets;
	}
	
	/**
	 * @return
	 */
	public WoJPAField getOrderableField () {
		for (WoJPAField f : this.getAllColumns()) {
			if (f.isOrderable()) {
				return f;
			}
		}
		return null;
	}
	
	/**
	 * @return
	 */
	public Boolean isOrderable () {
		return this.getOrderableField() != null;
	}
	
	/**
	 * @return
	 */
	public WoJPAField getFirstKeyField () {
		if (getKeys().size() > 0) {
			return getKeys().get(0);
		}
		return null;
	}
	
	/**
	 * @return
	 */
	public WoJPAField getFirstColumnField () {
		if (getColumns().size() > 0) {
			return getColumns().get(0);
		}
		return null;
	}
	
	/**
	 * 是否弱实体
	 * @return
	 */
	public Boolean isWeak () {
		for (WoJPARelation r : this.getRelationsM21()) {
			if (r.getRelatedRelation() == null) {
				continue;
			}
			if (r.getRelatedRelation().relateWeakEntity()) {
				return true;
			}
		}
		return false;
	}
	
	/**
	 * 当前实体如果是弱实体,则获取其弱关联M21
	 * @return
	 */
	public WoJPARelation getWeakRelationM21 () {
		for (WoJPARelation r : this.getRelationsM21()) {
			if (r.getRelatedRelation().relateWeakEntity()) {
				return r;
			}
		}
		return null;
	}
	
	/**
	 * @return
	 */
	public Boolean isIgnore () {
		return "true".equalsIgnoreCase(params.get("ignore"));
	}
}
